/*
 * Copyright (C) 2009 Google Inc.  All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zinnaworks.smart_cloud_gamepad;


import java.util.concurrent.TimeUnit;

import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.anymote.Key.Code;
import com.zinnaworks.smart_cloud_gamepad.ConnectionManager.ConnectionListener;
import com.zinnaworks.smart_cloud_gamepad.protocol.ICommandSender;
import com.zinnaworks.smart_cloud_gamepad.protocol.QueuingSender;
import com.zinnaworks.smart_cloud_gamepad.util.Debug;
import com.zinnaworks.smart_cloud_gamepad.widget.DualJoystickView.JoypadListener;

/**
 * Base for most activities in the app.
 * <p>
 * Automatically connects to the background service on startup.
 *
 */
public class BaseActivity extends CoreServiceActivity
implements ConnectionListener {

	private static final String LOG_TAG = "BaseActivity";

	/**
	 * Request code used by this activity.
	 */
	private static final int CODE_SWITCH_BOX = 1;

	/**
	 * Request code used by this activity for pairing requests.
	 */
	private static final int CODE_PAIRING = 2;

	private static final long MIN_TOAST_PERIOD = TimeUnit.SECONDS.toMillis(3);

	/**
	 * User codes defined in activities extending this one should start above
	 * this value.
	 */
	public static final int FIRST_USER_CODE = 100;

	/**
	 * Code for delayed messages to dim the screen.
	 */
	private static final int SCREEN_DIM = 1;

	/**
	 * Backported brightness level from API level 8 
	 */
	private static final float BRIGHTNESS_OVERRIDE_NONE = -1.0f;

	
	private final QueuingSender commands;

	private boolean isConnected;

	private boolean isKeepingConnection;

	private boolean isScreenDimmed;

	private Handler handler;

	public static ImageView dpadLeft;
	public static ImageView dpadRight;
	public static ImageView dpadTop;
	public static ImageView dpadBottom;

	/**
	 * Constructor.
	 */
	BaseActivity() {
		commands = new QueuingSender(new MissingSenderToaster());
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
		handler = new Handler(new ScreenDimCallback());

		//		trackballHandler = createTrackballHandler();
		//		trackballHandler.setAudioManager(am);

	}

	@Override
	protected void onStart() {
		super.onStart();
		setKeepConnected(true);
	}

	@Override
	protected void onStop() {
		setKeepConnected(false);
		super.onStop();
	}

	@Override
	protected void onResume() {
		super.onResume();
		connect();
		resetScreenDim();
	}

	@Override
	protected void onPause() {
		handler.removeMessages(SCREEN_DIM);
		disconnect();
		super.onPause();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuInflater inflater = new MenuInflater(this);
		inflater.inflate(R.menu.main, menu);
		return true;
	}

	// MENU HANDLER
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {

		case R.id.menu_switch:
			getConnectionManager().requestDeviceFinder();
			return true;

		default:
			return super.onOptionsItemSelected(item);
		}
	}



	@Override
	protected void onActivityResult(final int requestCode, final int resultCode,
			final Intent data)  {
		executeWhenCoreServiceAvailable(new Runnable() {
			public void run() {
				if (requestCode == CODE_SWITCH_BOX) {
					if (resultCode == RESULT_OK && data != null) {
						RemoteDevice remoteDevice =
								data.getParcelableExtra(DeviceFinder.EXTRA_REMOTE_DEVICE);
						if (remoteDevice != null) {
							getConnectionManager().setTarget(remoteDevice);
						}
					}
					getConnectionManager().deviceFinderFinished();
					connectOrFinish();
				}
				else if (requestCode == CODE_PAIRING) {
					getConnectionManager().pairingFinished();
					handlePairingResult(resultCode);
				}
			}
		});
	}

	private void showMessage(int resId) {
//		Toast.makeText(this, getString(resId), Toast.LENGTH_SHORT).show();
	}

	private void handlePairingResult(int resultCode) {
		switch (resultCode) {
		case PairingActivity.RESULT_OK:
			showMessage(R.string.pairing_succeeded_toast);
			connect();
			break;
		case PairingActivity.RESULT_CANCELED:
			getConnectionManager().requestDeviceFinder();
			break;
		case PairingActivity.RESULT_CONNECTION_FAILED:
		case PairingActivity.RESULT_PAIRING_FAILED:
			showMessage(R.string.pairing_failed_toast);
			getConnectionManager().requestDeviceFinder();
			break;
			
		case PairingActivity.RESULT_PAIRING_CANCEL:
			break;
		default:
			throw new IllegalStateException("Unsupported pairing activity result: "
					+ resultCode);
		}
	}

	/**
	 * Returns the interface to send commands to the remote box.
	 */
	protected final ICommandSender getCommands() {
		return commands;
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
			onKeyboardOpened();
		}
		if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
			onKeyboardClosed();
		}
	}

	/**
	 * Called when the physical keyboard is opened.
	 * <p>
	 * The default behavior is to close the current activity and to start the
	 * keyboard activity. Extending classes can override to change behavior.
	 */
	protected void onKeyboardOpened() {
		showActivity(KeyboardActivity.class);
		finish();
	}

	/**
	 * Called when the physical keyboard is closed.
	 * <p>
	 * Extending classes can override to change behavior.
	 */
	protected void onKeyboardClosed() {
		// default behavior is to do nothing
	}

	/**
	 * Returns {@code true} if the activity is in landscape mode.
	 */
	protected boolean isLandscape() {
		return (getResources().getConfiguration().orientation ==
				Configuration.ORIENTATION_LANDSCAPE);
	}

	//	/**
	//	 * Returns a default implementation for the DpadListener.
	//	 */
	//	protected DpadListener getDefaultDpadListener() {
	//		return new DpadListener() {
	//
	//			public void onDpadMoved(SoftDpad.Direction direction, boolean pressed) {
	//				Action action = translateDirection(direction, pressed);
	//				Log.d(LOG_TAG, "Action : " + action);
	//				if (action != null) {
	//					action.execute(getCommands());
	//				}
	//			}
	//			public void onDpadPoint(int x, int y) {
	//
	//				getCommands().moveRelative(x, y);
	//			}
	//		};
	//	};

	/**
	 * Returns a default implementation for the DpadListener.
	 */
	protected JoypadListener getDefaultJoypadListener() {
		return new JoypadListener() {

			@Override
			public void onJoypadPoint(int x, int y) {
				// TODO Auto-generated method stub

			}

			@Override
			public void onJoypadKeyPress(Code code) {
				// TODO Auto-generated method stub
				getCommands().keyPress(code);
			}

			@Override
			public void onJoypadClicked() {
				// TODO Auto-generated method stub

			}
		};
	};



	private void connect() {
		if (!isConnected) {
			isConnected = true;
			executeWhenCoreServiceAvailable(new Runnable() {
				public void run() {
					getConnectionManager().connect(BaseActivity.this);
				}
			});
		}
	}

	private void disconnect() {
		if (isConnected) {
			commands.setSender(null);
			isConnected = false;
			executeWhenCoreServiceAvailable(new Runnable() {
				public void run() {
					getConnectionManager().disconnect(BaseActivity.this);
				}
			});
		}
	}

	private void setKeepConnected(final boolean keepConnected) {
		if (isKeepingConnection != keepConnected) {
			isKeepingConnection = keepConnected;
			executeWhenCoreServiceAvailable(new Runnable() {
				public void run() {
					logConnectionStatus("Keep Connected: " + keepConnected);
					getConnectionManager().setKeepConnected(keepConnected);
				}
			});
		}
	}

	/**
	 * Starts the box selection dialog.
	 */
	private final void showSwitchBoxActivity() {
		
		disconnect();
		startActivityForResult(
				DeviceFinder.createConnectIntent(this, getConnectionManager().getTarget(),
						getConnectionManager().getRecentlyConnected()), CODE_SWITCH_BOX);
	}

	/**
	 * If connection failed due to SSL handshake failure, this method will be
	 * invoked to start the pairing session with device, and establish secure
	 * connection.
	 * <p>
	 * When pairing finishes, PairingListener's method will be called to
	 * differentiate the result.
	 */
	private final void showPairingActivity(RemoteDevice target) {
		disconnect();
		if (target != null) {
			startActivityForResult(
					PairingActivity.createIntent(this, new RemoteDevice(
							target.getName(), target.getAddress(), target.getPort() + 1)),
							CODE_PAIRING);
		}
	}
	
	

	public void onConnecting() {
		commands.setSender(null);
		logConnectionStatus("Connecting");
	}

	public void onShowDeviceFinder() {
		commands.setSender(null);
		logConnectionStatus("Show device finder");
		showSwitchBoxActivity();
	}

	public void onConnectionSuccessful(ICommandSender sender) {
		logConnectionStatus("Connected");
		commands.setSender(sender);
	}
	
	public void onConnectionStbId(String stbId){
		
	}

	public void onNeedsPairing(RemoteDevice remoteDevice) {
		logConnectionStatus("Pairing");
		showPairingActivity(remoteDevice);
	}
	
	public void onDisconnected() {
		commands.setSender(null);
		logConnectionStatus("Disconnected");
	}

	private class MissingSenderToaster
	implements QueuingSender.MissingSenderListener {
		private long lastToastTime;

		public void onMissingSender() {
			if (System.currentTimeMillis() - lastToastTime > MIN_TOAST_PERIOD) {
				lastToastTime = System.currentTimeMillis();
				showMessage(R.string.sender_missing);
			}
		}
	}

	private void logConnectionStatus(CharSequence sequence) {
		String message = String.format("%s (%s)", sequence,
				getClass().getSimpleName());
		if (Debug.isDebugConnection()) {
			Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
		}
		Log.d(LOG_TAG, "Connection state: " + sequence);
	}

	private void connectOrFinish() {
		if (getConnectionManager() != null) {
			if (getConnectionManager().getTarget() != null) {
				connect();
			} else {
				finish();
			}
		}
	}

	@Override
	protected void onServiceAvailable(CoreService coreService) {
	}

	@Override
	protected void onServiceDisconnecting(CoreService coreService) {
		disconnect();
		setKeepConnected(false);
	}

	// Screen dimming

	@Override
	public void onUserInteraction() {
		super.onUserInteraction();
		resetScreenDim();
	}

	private void screenDim() {
		if (!isScreenDimmed) {
			WindowManager.LayoutParams lp = getWindow().getAttributes();
			lp.screenBrightness = getResources().getInteger(
					R.integer.screen_brightness_dimmed) / 100.0f;
			getWindow().setAttributes(lp);
		}
		isScreenDimmed = true;
	}

	private void resetScreenDim() {
		if (isScreenDimmed) {
			WindowManager.LayoutParams lp = getWindow().getAttributes();
			lp.screenBrightness = BRIGHTNESS_OVERRIDE_NONE;
			getWindow().setAttributes(lp);
		}
		isScreenDimmed = false;
		handler.removeMessages(SCREEN_DIM);
		handler.sendEmptyMessageDelayed(SCREEN_DIM,
				TimeUnit.SECONDS.toMillis(getResources().getInteger(
						R.integer.timeout_screen_dim)));
	}

	private class ScreenDimCallback implements Handler.Callback {

		public boolean handleMessage(Message msg) {
			switch (msg.what) {
			case SCREEN_DIM:
				screenDim();
				return true;
			}
			return false;
		}
	}
}
